package nl.Viewer.ViewManager.Models.DataStores;

import java.awt.Image;
import java.awt.image.PixelGrabber;
import java.io.ByteArrayInputStream;

import javax.imageio.ImageIO;

import nl.Viewer.SerializedObjects.DataObject;
import nl.Viewer.ViewManager.Objects.RendererHelperFunctions;

/**
 * An implementation of an ImageDataStore (which itself is an extension of a DataStore) used to handle Zoomify-ed images
 * and correctly convert them into imageDataObject data
 * 
 * @author Jeroen Verschuur
 * 
 */

public class ZoomifyDataStore extends ImageDataStore {
   public ZoomifyDataStore( DataObject dataObject ) {
      super( dataObject );
   }

   public synchronized void addData( Object data, int resolution, int xPos, int yPos ) {
      // create an image from the data object
      try {
         byte[] imagedata = (byte[])data;

         Image image = ImageIO.read( new ByteArrayInputStream( imagedata ) );
         imagedata = null;         
         int w = image.getWidth( null );
         int h = image.getHeight( null );

         int[] pixels = new int[ w * h ];

         PixelGrabber pg = new PixelGrabber( image, 0, 0, w, h, pixels, 0, w );
         pg.grabPixels();
         int[] mirror = new int[ w * h ];

         for( int yp = 0; yp < h; yp++ ) {
            for( int xp = 0; xp < w; xp++ ) {
               mirror[( yp * w ) + xp] = RendererHelperFunctions.convertPixel( pixels[( yp * w ) + xp] );
            }
         }
         pixels = null;
         image = null;
         // System.out.println( "addData to ZoomifyDataStore: W = " + w + ", H = " + h + ", tilesize: "
         // + dataDefinition.getTilesize() + ", resolutions: " + dataDefinition.getResolutionLevelCount() );

         int factor = (int)( Math.pow( 2, ( dataDefinition.getResolutionLevelCount() - resolution - 1 ) ) );

         int totalWidth = dataDefinition.getBounds().width;
         int totalHeight = dataDefinition.getBounds().height;

         // find start coordinate for specified xPos in the image space
         int xStart = xPos * dataDefinition.getTilesize() * factor;
         int yStart = yPos * dataDefinition.getTilesize() * factor;
         int xEnd = xStart + ( dataDefinition.getTilesize() * factor );
         if( xEnd > totalWidth )
            xEnd = totalWidth;

         int yEnd = yStart + ( dataDefinition.getTilesize() * factor );
         if( yEnd > totalHeight )
            yEnd = totalHeight;

         ImageDataObject ido = ( (ImageDataObject)currentData );

         // we should always synchronize on the imageDataObject to prevent access by for example the views
         synchronized( ido ) {
            // account for roundoff errors
            double actualXFactor = (double)( xEnd - xStart ) / (double)w;
            double actualYFactor = (double)( yEnd - yStart ) / (double)h;

            for( int y = yStart; y < yEnd; y++ ) {
               for( int x = xStart; x < xEnd; x++ ) {

                  // map object-coordinate to data-part-coordinate and (unfortunately) account for the roundoff errors
                  int mappedX = (int)( (double)( x - xStart ) / actualXFactor );
                  int mappedY = (int)( (double)( y - yStart ) / actualYFactor );

                  // take care that the opengl frame is upside down
                  ido.pixelData[( ( totalHeight - 1 - y ) * totalWidth ) + x] = mirror[mappedX + ( mappedY * w )];

                  // ido.data.position( ( ( totalHeight - 1 - y ) * totalWidth ) + x );
                  // ido.data.put( mirror[mappedX + ( mappedY * w )] );

               }
            }
         }
         mirror = null;
      }
      catch( Exception e ) {
         System.out.println( "Error in creating image from byte[]: " + e );
         e.printStackTrace();
         return;
      }
   }

}
